#pragma
#include<iostream>
#include<string>
#include<cerrno>
#include<cstring>
#include<cstdlib>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
using namespace std;

class udpClient
{
public:

    udpClient(const string& serverip, const uint16_t& serverport)
    :_sockfd(-1)
    ,_serverip(serverip)
    ,_serverport(serverport)
    ,_quit(false)
    {}

    void clientInit()
    {
        // 1.创建socket(套接字)
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd == -1)
        {
            cerr << "socket error: " << errno << ": " << strerror(errno) << endl;
            exit(2);
        }
        cout << "socket success: " << _sockfd << endl;
        // client需要bind吗？需要程序员自己显式bind吗？
        // 答案是，client需要bind，但是不需要显式bind。OS自动会形成客户端的port进行bind。为什么？
        // 1.因为写服务器的是一家公司，写客户端的是无数家公司。如果每个客户端都指定自己要固定的port，那很可能会出现port冲突的情况
        // 2.服务器的port很重要，是明确的被众人知晓的，因为客户端都要往服务器传输数据，但是客户端的port不重要，只要随便给一个(保证唯一性)，能让服务器找到你，给你传输数据就行了

        // 因此这里不需要手动bind，OS会检测到该套接字没有bind，就会自动生成ip和port，然后bind
        // 但是OS什么时候bind，如何bind呢？
        // 其实是在客户端第一次向服务器端发送数据的时候，OS检测到，怎么只有目的ip和目的port，就会自动生成ip和port，并bind
    }

    void run()
    {
        // 客户端要能完成数据的读写
        struct sockaddr_in server; //填充该结构体对象
        server.sin_addr.s_addr = inet_addr(_serverip.c_str()); //转成4字节ip，并转化为网络字节序
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport); //htons转化为网络字节序

        string message;
        char buffer[1024];
        while(!_quit)
        {
            cout << "Enter message: ";
            // cin >> message;
            // cout << message << endl;
            fgets(buffer, sizeof(buffer), stdin);
            message = buffer; 
            
            // 将message发送给服务器
            sendto(_sockfd, message.c_str(), sizeof(message), 0, (struct sockaddr*)&server, sizeof(server));
            // 接受服务器处理完毕的数据
            char receive[1024]; // 接受服务器处理完毕的数据
            // 创建temp和len拿到服务器的信息
            struct sockaddr_in temp;
            socklen_t len = sizeof(temp);
            size_t n = recvfrom(_sockfd, receive, sizeof(receive)-1, 0, (struct sockaddr*)&temp, &len);
            if(n >= 0)
                receive[n] = 0; //当字符串来用
            cout << "服务器处理后返回：\n" << receive << endl;
        }
    }

    ~udpClient()
    {}


private:
    int _sockfd; //socket函数返回的文件描述符
    string _serverip; //目的服务器ip
    uint16_t _serverport; // 目的服务器端口号
    bool _quit;
};